/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2021/06/06
 * Description        : Main program body.
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/*
 *@Note
 USART Print debugging routine:
 USART1_Tx(PA9).
 This example demonstrates using USART1(PA9) as a print debug port output.

*/

#include "debug.h"
#include "HMC544A/HMC544.h"
#include "LED/LED.h"
#include "AD9910/ad9910.h"
#include "USB_Device/ch32v30x_usbfs_device.h"
#include "PE4302/pe4302.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
extern u8 rxBuffer[];

/* Global typedef */

/* Global define */

/* Global Variable */


/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
typedef enum {
    SIN = 0,
    PULSE,
    DBLPULSE,
    TRIANGULAR,
    SINGLEUPSCAN,
    SINGLEDOWNSCAN,
    SINGLEUPSCANREPEAT,
    SINGLEDOWNSCANREPEAT,
    BIDIRECTIONAL,
    BODE_SCAN,
    SEL_INPUT,
    SET_GAIN,
    END,
} CMDS;

typedef union {
    struct {
        u16 adc0;
        u16 adc1;
    };

    u32 value;
} TADCResult;

TADCResult AdcResult;

typedef u8 (*CMD) (u32 *);

u8 gen_Sin (u32 *p) {
    AD_GenerateSinWave (p[0], p[1]);
    return 1;
}

u8 gen_pulse (u32 *p) {
    AD_GenerateSquareWave (p[0], p[1], p[2]);
    return 1;
}

u8 gen_dblpulse (u32 *p) {
    AD_GenerateDblSquareWave (p[0], p[1], p[2]);
    return 1;
}

u8 gen_triangle (u32 *p) {
    AD_GenerateTriangularwave (p[0], p[1], p[2]);
    return 1;
}

u8 bode_scan (u32 *p) {
    // 配置常量
    const u32 MIN_CYCLE_US = 33000;                            // 最小采样周期
    const u32 FILTER_SETTLE_US = 100;                        // 滤波器稳定时间
    const u32 SAMPLING_INTERVAL_US = FILTER_SETTLE_US - 22;  // 采样间隔
    const u32 NS_TO_US_SCALE = 1000;                         // 周期计算缩放因子

    // 初始化参数
    const u32 scan_points = p[1];
    const u32 amplitude = p[0];
    u32 *p_ftw = &p[2];
    u32 *result_buffer = &p[0];

    PROFILE dds_profile = {0};
    AD_BodeScan (amplitude, *p_ftw, &dds_profile);
    Delay_Ms (1000);

    // 主扫描循环
    for (u32 point_idx = 0; point_idx < scan_points; point_idx++) {
        // 计算当前频率周期
        const u64 ftw_value = *p_ftw++;
        const u32 signal_period_us = ((u64)1 << 32) / NS_TO_US_SCALE / ftw_value;

        // 配置DDS输出
        AD_BodeScan (amplitude, ftw_value, &dds_profile);

        // 计算采样参数
        const u32 effective_period = MAX (signal_period_us, MIN_CYCLE_US);
        const u32 sample_count = effective_period / FILTER_SETTLE_US;

        // 数据采集
        u64 sum_adc0 = 0, sum_adc1 = 0;
        for (u32 sample_idx = 0; sample_idx < sample_count; sample_idx++) {
            ADC_SoftwareStartConvCmd (ADC1, ENABLE);
            while (DMA_GetITStatus (DMA1_IT_TC1) == RESET);
            DMA_ClearITPendingBit (DMA1_IT_GL1);

            sum_adc0 += AdcResult.adc0;
            sum_adc1 += AdcResult.adc1;
            Delay_Us (SAMPLING_INTERVAL_US);
        }

        // 存储平均结果
        TADCResult processed_result = {
            .adc0 = sum_adc0 / sample_count,
            .adc1 = sum_adc1 / sample_count};
        result_buffer[point_idx] = processed_result.value;
    }

    // 结束处理
    ((PMessage)rxBuffer)->len = (scan_points + 1) << 2;
    AD_BodeStop (&dds_profile);
    USB_StartSend();
    return 0;
}

u8 sel_input (u32 *p) {
    if (p[0] == 1) {
        PSASelSMA();
    } else {
        PSASelBNC();
    }
    SetAttenuation (p[1], 1);
    if (p[2] == 1) {
        PSBSelSMA();
    } else {
        PSBSelBNC();
    }
    SetAttenuation (p[3], 2);
    return 1;
}

u8 single_upscan (u32 *p) {
    AD_Scan_Up (p[0], p[1], p[2], p[3], p[4], 0);
    return 1;
}

u8 single_upscan_repeat (u32 *p) {
    AD_Scan_Up (p[0], p[1], p[2], p[3], p[4], 1);
    return 1;
}

u8 single_downscan (u32 *p) {
    AD_Scan_Down (p[0], p[2], p[1], p[3], p[4], 0);
    return 1;
}

u8 single_downscan_repeat (u32 *p) {
    AD_Scan_Down (p[0], p[2], p[1], p[3], p[4], 1);
    return 1;
}

u8 bi_upDownScan (u32 *p) {
    AD_Scan_UpDown (p[0], p[1], p[2], p[3], p[4], p[5]);
    return 1;
}

CMD commands[] = {
    gen_Sin,
    gen_pulse,
    gen_dblpulse,
    gen_triangle,
    single_upscan,
    single_downscan,
    single_upscan_repeat,
    single_downscan_repeat,
    bi_upDownScan,
    bode_scan,
    sel_input,
};

u8 cmd_exec (u16 cmd, void *param) {
    AD_StopAll();
    if (cmd >= SIN && cmd < END)
        return commands[cmd](param);
    else
        return 0;
}

s16 Calibrattion_Val1;
s16 Calibrattion_Val2;

void ADC_Function_Init (void) {
    ADC_InitTypeDef ADC_InitStructure = {0};
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1, ENABLE);
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC2, ENABLE);
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init (GPIOA, &GPIO_InitStructure);

    ADC_DeInit (ADC1);
    ADC_DeInit (ADC2);

    ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_InitStructure.ADC_OutputBuffer = ADC_OutputBuffer_Disable;
    ADC_InitStructure.ADC_Pga = ADC_Pga_1;

    ADC_Init (ADC1, &ADC_InitStructure);
    ADC_RegularChannelConfig (ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);

    ADC_DMACmd (ADC1, ENABLE);
    ADC_Cmd (ADC1, ENABLE);

    ADC_BufferCmd (ADC1, DISABLE);  // disable buffer
    ADC_ResetCalibration (ADC1);
    while (ADC_GetResetCalibrationStatus (ADC1));
    ADC_StartCalibration (ADC1);
    while (ADC_GetCalibrationStatus (ADC1));
    Calibrattion_Val1 = Get_CalibrationValue (ADC1);

    ADC_Init (ADC2, &ADC_InitStructure);
    ADC_RegularChannelConfig (ADC2, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);

    ADC_SoftwareStartConvCmd (ADC2, ENABLE);
    ADC_Cmd (ADC2, ENABLE);

    ADC_BufferCmd (ADC2, DISABLE);  // disable buffer
    ADC_ResetCalibration (ADC2);
    while (ADC_GetResetCalibrationStatus (ADC2));
    ADC_StartCalibration (ADC2);
    while (ADC_GetCalibrationStatus (ADC2));
    Calibrattion_Val2 = Get_CalibrationValue (ADC2);
}

void DMA_Tx_Init (DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize) {
    DMA_InitTypeDef DMA_InitStructure = {0};

    RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit (DMA_CHx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init (DMA_CHx, &DMA_InitStructure);
}

void reset (void) {
    AD_Init();
    ADC_Function_Init();
    DMA_Tx_Init (DMA1_Channel1, (u32)&ADC1->RDATAR, (u32)&AdcResult, 1);
    DMA_Cmd (DMA1_Channel1, ENABLE);
    pe4302Init();
    HMC544Init();
    LED_Init();
    SetAttenuation (0, 1);
    SetAttenuation (0, 2);
}

int main (void) {
    NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USBFS_RCC_Init();
    USBFS_Device_Init (ENABLE);
    reset();
    while (1) {
    }
}
